#include <iostream>
#include <string>
#include <regex>

// 正则表达式：一种字符串匹配模式   正则库：给我们提供了一系列接口，让我们能够通过接口来实现正则表达式的匹配
int main()
{
    // std::string str = "/numbers/12345";
    // // 目的：提取str1中的数字字符串
    // // 正则表达式匹配流程: 根据regex字符串的匹配规则，从左向右逐个遍历str1的各个字符串，符合regex匹配规则保留下来，不符合则匹配终止失败
    // std::regex e("/numbers/(\\d+)");
    // // 1.双反斜杠的原因:
    // // C++语言级别看到反斜杠会立马转义一次，在regex引擎里面\d表示匹配一个0-9的数字字符，如果你直接写\d+，那么在C++语言这一层，
    // // 就会将字符串解释为d+了，而到了regex引擎那层开始分析字符串时，就只剩d+了，regex此时就会解释为要匹配d这个字符一次或多次了
    // // 此时就不满足我们的需求了，所以我们需要两个反斜杠，先让第一个反斜杠在C++这一层转义一次，保证传递的是\d+到regex引擎
    // // 2.当想要提取某一段子串时，在regex里面，直接使用括号括起来即可
    // // 3.regex的含义:
    // // 表示匹配以/numbers/为开头的，后面跟着一个或多个数字的字符串，+号表示的是匹配一次或多次，并且在正则匹配的过程中，
    // // 将匹配到的数字字符串提取出来存放到matches中，但最先放到matches里面的会是整个原始的字符串，然后才是存放我们要提取的字符串
    // std::smatch matches;
    // bool ret = std::regex_match(str, matches, e);//开始正则匹配，将结果put to matches中
    // if (ret == false)
    // {
    //     return -1;
    // }
    // for(auto &s : matches)
    // {
    //     std::cout << s << std::endl;
    // } 

    // 目的：使用正则表达式对http请求行进行解析
    std::string req = "GET /csdn/login?user=xiaoming&pass=123123 HTTP/1.1";
    std::smatch matches; // smatch类似于一种数组，将匹配后的结果存储到smatch定义的对象里面

    std::regex e1("(GET|HEAD|POST|PUT|DELETE).*");         //.不能匹配\n这样的字符，所以我们先把req末尾的\r\n去掉
    std::regex e2("(GET|HEAD|POST|PUT|DELETE) ([^?]*).*"); //[^?]*匹配0次或多次非？的字符
    std::regex e3("(GET|HEAD|POST|PUT|DELETE) ([^?]*)\\?(.*) .*");
    // 想要在regex引擎中匹配？则需要转义一次，又由于C++会先转义一次，因为我们想传给regex引擎的是\?，所以要双反斜杠
    // \\?(.*) 匹配从?开始的字符串，直到空格停下来，提取空格前，问号后，之间的子串
    std::regex e4("(GET|HEAD|POST|PUT|DELETE) ([^?]*)\\?(.*) (HTTP/1\\.[01])");
    //[01]表示匹配0或1的任意一个字符，如果req里从左到右先出现1，那就匹配1，先出现0，那就匹配0
    // 因为.这个符号在regex里面有特殊意义，所以我们希望传给regex的是\. 但C++看到\后，会率先进行转义一次，那传给regex引擎的就是.了
    // 这不符合我们的需求，所以为了补充上C++转义这一层，我们多加一个反斜杠

    // 在req末尾加上\r\n
    req = "GET /csdn/login?user=xiaoming&pass=123123 HTTP/1.1\r\n";
    // 有可能请求行不标准，结尾不是标准的\r\n，而是\n，所以结尾元素可能是\r\n或\n
    std::regex e5("(GET|HEAD|POST|PUT|DELETE) ([^?]*)\\?(.*) (HTTP/1\\.[01])(?:\n|\r\n)?");
    // 我们想匹配到req末尾的\r\n,但我们又不希望将这个\r\n提取出来，那就可以使用?:来进行:后面的正则匹配规则，但不会提取?:后面匹配到的内容
    //(?:\n|\r\n)?结尾的这个?表示匹配前面括号中的内容0次或1次，也就是说如果req末尾有\r\n或\n那就匹配到，但不提取，如果末尾没有\n也没有\r\n
    // 那么结尾的?就匹配前面括号中内容0次，也就是不匹配。总结：req末尾有那我就匹配，没有就不匹配了
    std::regex e6("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?");
    //(\\?(.*))?以?开始的字符串可以匹配也可以不匹配，因为请求行中uri后面可能没有查询字符串，只有请求路径而已，所以我们把这个子表达式
    //括起来，然后末尾加个?表示前面的这部分表达式可以匹配也可以不匹配，但这样的话，还有一个问题，两个括号就会提取查询字符串和?在查询字符串前面
    //的这个子串了，但我们不想要?开头的那个子串，所以在括号内部加个?:表示匹配但不提取，而括号中的内部括号，也就是匹配查询字符串的那个括号，
    //他是可以正确提取出来查询字符串的。

    bool ret = std::regex_match(req, matches, e6);
    if (ret == false)
    {
        printf("regex match failed!");
        return -1;
    }
    for (int i = 0; i < matches.size(); i++)
    {
        std::cout << i << ":" << matches[i] << std::endl;
    }

    return 0;
}